import logging
from datetime import date, datetime, timedelta
from decimal import Decimal
from typing import Any, Literal, LiteralString

from auditlog.models import AuditlogHistoryField
from auditlog.registry import auditlog
from django.contrib import admin
from django.contrib.auth.models import Group, User
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import F, Value
from django.db.models.functions import Concat
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.utils import timezone
from django.utils.html import format_html
from django.utils.safestring import SafeText


class Base(models.Model):
    create_at = models.DateTimeField(default=timezone.now)
    update_at = models.DateTimeField(auto_now=True)
    history = AuditlogHistoryField()

    class Meta:
        abstract = True


class ProductSection(Base):
    name = models.CharField(max_length=100, verbose_name="Название группы продуктов", unique=True)

    class Meta:
        verbose_name = "Группы продуктов"
        verbose_name_plural = "Группы продуктов"

    def __str__(self) -> str:
        return f"{self.name}"


class Product(Base):
    name = models.CharField(max_length=100, verbose_name="Название продукта", unique=True)
    product_section = models.ForeignKey(ProductSection, verbose_name="Группа продуктов", on_delete=models.CASCADE)
    warehouse = models.DecimalField(max_digits=8, decimal_places=2, default=0.0, verbose_name="Остаток на складе")
    measure = models.CharField(max_length=20, default="", blank=False, verbose_name="Единицы измерения")

    class Meta:
        verbose_name = "Продукты"
        verbose_name_plural = "Продукты"

    def __str__(self) -> str:
        return f"{self.name}"


class Contract(Base):
    organization_name = models.TextField(max_length=500, verbose_name="Организация")
    contract_number = models.CharField(max_length=100, verbose_name="Номер договора")
    is_closed = models.BooleanField(default=False, verbose_name="Расторгнут")
    start_date = models.DateField(verbose_name="Дата начала договора")
    end_date = models.DateField(verbose_name="Дата окончания договора")

    class Meta:
        verbose_name = "Договора"
        verbose_name_plural = "Договора"
        unique_together = ("contract_number", "start_date")

    def __str__(self) -> str:
        return f"{"З" if not self.is_active else "Д"} {self.organization_name} {self.contract_number} {self.start_date}"

    @property
    def is_active(self) -> bool:
        if (self.end_date - datetime.now().date()).days >= 0:  # noqa: DTZ005, SIM103
            return True
        return False
    is_active.fget.short_description = "Активен"


class ContractProduct(Base):
    contract = models.ForeignKey(Contract, verbose_name="Договор", on_delete=models.CASCADE)
    oms_full = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="ОМС по контракту")
    oms_balance = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="ОМС остаток")
    oms_percent = models.GeneratedField(expression=(F("oms_balance") / F("oms_full"))*100, output_field=models.PositiveIntegerField(), db_persist=True, verbose_name="ОМС остаток в %")
    ob_full = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="ОБ по контракту")
    ob_balance = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="ОБ остаток")
    ob_percent = models.GeneratedField(expression=(F("ob_balance") / F("ob_full"))*100, output_field=models.PositiveIntegerField(), db_persist=True, verbose_name="ОБ остаток в %")
    pd_full = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="ПД по контракту")
    pd_balance = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="ПД остаток")
    pd_percent = models.GeneratedField(expression=(F("pd_balance") / F("pd_full"))*100, output_field=models.PositiveIntegerField(), db_persist=True, verbose_name="ПД остаток в %")
    product = models.ForeignKey(Product, verbose_name="Продукт", on_delete=models.CASCADE)

    class Meta:
        verbose_name = "Лимиты по продуктам"
        verbose_name_plural = "Лимиты по продуктам"

    def __str__(self) -> str:
        return f"{self.product} {self.contract} {self.oms_balance} {self.ob_balance} {self.pd_balance}"

FOND_CHOICES = {
    "oms": "ОМС",
    "ob": "ОБ",
    "pd": "ПД",
    "out": "Расход",
}

class StockRecord(Base):
    id = models.AutoField(primary_key=True, verbose_name="Номер документа")
    contract_product = models.ForeignKey(ContractProduct, verbose_name="Договор", on_delete=models.CASCADE)
    document_date = models.DateField(verbose_name="Дата")
    incoming_weight = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Входящий вес")
    outgoing_weight = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Исходящий вес")
    stock_code = models.CharField(max_length=100, choices=FOND_CHOICES, verbose_name="Код оплаты")
    incoming_document = models.TextField(default = "")

    class Meta:
        verbose_name = "Документы на списание"
        verbose_name_plural = "Документы на списание"

    def __str__(self) -> str:
        return f"{self.document_date} {self.id}"


@receiver(post_save, sender=StockRecord)
def after_add_stock(sender, instance, created, **kwargs) -> None:  # noqa: ANN003, ARG001
    if created:
        if instance.stock_code == "oms":
            ContractProduct.objects.filter(id=instance.contract_product.id).update(oms_balance=F("oms_balance") - instance.incoming_weight)
            product = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
            product.warehouse = product.warehouse + instance.incoming_weight
            product.save()
        if instance.stock_code == "ob":
            ContractProduct.objects.filter(id=instance.contract_product.id).update(ob_balance=F("ob_balance") - instance.incoming_weight)
            product = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
            product.warehouse = product.warehouse + instance.incoming_weight
            product.save()
        if instance.stock_code == "pd":
            ContractProduct.objects.filter(id=instance.contract_product.id).update(pd_balance=F("pd_balance") - instance.incoming_weight)
            product = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
            product.warehouse = product.warehouse + instance.incoming_weight
            product.save()
        if instance.stock_code == "out":
            product  = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
            product.warehouse -= instance.outgoing_weight
            product.save()


@receiver(post_delete , sender=StockRecord)
def after_delete_stock(sender, instance, **kwargs) -> None:  # noqa: ANN003, ARG001
    if instance.stock_code == "oms":
        ContractProduct.objects.filter(id=instance.contract_product.id).update(oms_balance=F("oms_balance") + instance.incoming_weight)
        product = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
        product.warehouse = product.warehouse - instance.incoming_weight
        product.save()
    if instance.stock_code == "ob":
        ContractProduct.objects.filter(id=instance.contract_product.id).update(ob_balance=F("ob_balance") + instance.incoming_weight)
        product = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
        product.warehouse = product.warehouse - instance.incoming_weight
        product.save()
    if instance.stock_code == "pd":
        ContractProduct.objects.filter(id=instance.contract_product.id).update(pd_balance=F("pd_balance") + instance.incoming_weight)
        product = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
        product.warehouse = product.warehouse - instance.incoming_weight
        product.save()
    if instance.stock_code == "out":
        product  = ContractProduct.objects.filter(id=instance.contract_product.id).first().product
        product.warehouse += instance.outgoing_weight
        product.save()


class Groups(Base):
    group = models.CharField(max_length=100, verbose_name="Группа", unique=True)

    class Meta:
        verbose_name = "Группы сотрудников"
        verbose_name_plural = "Группы сотрудников"

    def __str__(self) -> str:
        return f"{self.group}"


class Users(Base):
    user = models.OneToOneField(User, verbose_name="Пользователь", on_delete=models.CASCADE)
    fam = models.CharField(verbose_name="Фамилия", max_length=50, null=False)
    im = models.CharField(verbose_name="Имя", max_length=50, null=False)
    ot = models.CharField(verbose_name="Отчество", max_length=50, default = "")
    dolzh = models.CharField(verbose_name="Должность", max_length=50, null=False)
    groups = models.ManyToManyField(Groups, verbose_name="Группы")

    class Meta:
        verbose_name = "Сотрудники"
        verbose_name_plural = "Сотрудники"

    def __str__(self) -> str:
        return f"{self.fam} {self.im[0]} {self.ot[0]} {self.groups}"

    @property
    def fio_short(self) -> str:
        return f"{self.fam} {self.im[0]} {self.ot[0]}"

    @property
    def groupa(self) -> str:
        return (", ".join(self.groups.all().values_list("group", flat=True)))
